package org.example.service.impl;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.lettuce.core.api.async.RedisTransactionalAsyncCommands;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import org.example.entity.*;
import org.example.exception.LoginException;
import org.example.mapper.MenuMapper;
import org.example.mapper.RoleMapper;
import org.example.mapper.UserMapper;
import org.example.security.UserDetailsImpl;
import org.example.service.IMenuService;
import org.example.service.IRoleService;
import org.example.service.IUserService;
import org.example.vo.LoginVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.time.Duration;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
public class TokenService {
    private final UserMapper userService;
    private final RoleMapper iRoleService;
    private final MenuMapper iMenuService;
    private final StringRedisTemplate  stringRedisTemplate;
    private final RedisTemplate redisTemplate;

    private static final String LOGIN_KEY = "s54:rbac:login:";

    /**
     * 1.根据登录名查询用户
     * 比对密码
     * 比对状态
     * 获取用户角色列表
     * 获取用户的权限列表
     * 生成token，存储用户和角色，权限到redis
     * @param loginVO
     * @return
     */
    @SneakyThrows
    public Token createToken(LoginVO loginVO) {
        User user=userService.selectOne(Wrappers.lambdaQuery(User.class).eq(User::getUserName,loginVO.getName())); // 按名字查询，用户名等于登录名
        if (user==null)throw new LoginException("用户不存在");
        if (!user.getPassword().equals(loginVO.getPassword()))throw new LoginException("密码不正确");
        if (user.getStatus().equals("0"))throw new LoginException("用户未激活");
        // 获取用户所有的角色列表
        List<Role> roleList=iRoleService.selectbyUserId(user.getUserId()); // 根据用户id找到所有的角色
        // 循环角色，把角色id一个个 取出来，放到数组里面
        Long[] roleIds= roleList.stream().mapToLong(Role::getRoleId).boxed().toArray(Long[]::new);
        // 根据角色获取所有菜单
        List<Menu> menuList=iMenuService.selectByRoleIds(roleIds);
        // 存储到redis
        String uuid= UUID.randomUUID().toString();
        Set<String> roleNames=roleList.stream().map(Role::getRoleKey).collect(Collectors.toSet());
        Set<String> perms=menuList.stream().filter(menu -> StringUtils.hasText(menu.getPerms())).map(Menu::getPerms).collect(Collectors.toSet());
        LoginUser loginUser=new LoginUser(roleNames,perms,user);
        String json=new ObjectMapper().writeValueAsString(loginUser);
        stringRedisTemplate.opsForValue().set(LOGIN_KEY+uuid,json, Duration.ofMinutes(30));
        Token token=new Token(System.currentTimeMillis(),System.currentTimeMillis()+1800000,uuid,user.getNickName());
        return token;
    }

    public void removeToken(String token) {
        stringRedisTemplate.delete(LOGIN_KEY+token);
    }

    @SneakyThrows
    public boolean hasPerms(String prem) {
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        // 获得请求头里面的token
        String token = requestAttributes.getRequest().getHeader("token");
        if (StringUtils.isEmpty(token))return false;

        String json = stringRedisTemplate.opsForValue().get(LOGIN_KEY + token);
        if (StringUtils.isEmpty(json))return false;

        LoginUser loginUser=new ObjectMapper().readValue(json,LoginUser.class);
        Set<String> permsAndRoles=new HashSet<>();
        permsAndRoles.addAll(loginUser.getPerms());
        permsAndRoles.addAll(loginUser.getRoleNames());
        return permsAndRoles.contains(prem); // 登录用户的集合是否包含了prem字符串
    }

    @SneakyThrows
    public Token createToken(UserDetailsImpl authenticate) {
        // 存储到redis
        String uuid= UUID.randomUUID().toString();
//        String json=new ObjectMapper().writeValueAsString(authenticate); // 把用户详情转换成json
        redisTemplate.opsForValue().set(LOGIN_KEY+uuid,authenticate, Duration.ofMinutes(30));
        Token token=new Token(System.currentTimeMillis(),System.currentTimeMillis()+1800000,uuid,authenticate.getUser().getNickName());// 返回昵称到前端
        return token;
    }

    @SneakyThrows
    public UserDetailsImpl getLoginUser(String token){
//        String json=stringRedisTemplate.opsForValue().get(LOGIN_KEY+token);
//        if (json!=null){
//            return new ObjectMapper().readValue(json,UserDetailsImpl.class);
//        }
        return (UserDetailsImpl) redisTemplate.opsForValue().get(LOGIN_KEY+token);
    }
}
